#! /bin/sh
# PCP QA Test No. 113
# check filesys metrics against df and mount
#
# Copyright (c) 1995-2002 Silicon Graphics, Inc.  All Rights Reserved.
#

seq=`basename $0`
echo "QA output created by $seq"

# get standard filters
. ./common.product
. ./common.filter
. ./common.check

_need_metric filesys.capacity
if [ $PCP_PLATFORM = linux ]
then
    loopcount=`$sudo losetup -a | wc -l`
    [ $loopcount -eq 0 ] || _notrun "System has active loop devices, bailing"
    overlayfs=`mount | grep '^overlay on / type overlay' | wc -l`
    [ $overlayfs -eq 0 ] || _notrun "System has an overlay root fs, bailing"
fi

status=0
trap "rm -f $tmp.*; exit \$status" 0 1 2 3 15

rm -f $seq.full

# On Linux mount output looks like
# /dev/sdb5 on /bozo-0 type ext4 (rw,relatime)
# On FreeBSD mount output looks like
# /dev/da0p2 on / (ufs, local, journaled soft-updates)
# on OpenBSD mount output looks like (same as Linux)
# /dev/wd0a on / type ffs (local)
#
# List of "types" (efs, xfs, ...) needs to match those filesystem types
# that in the instance domain for the filesys.* metrics.
#
echo "-- mount --" >>$seq.full
filesystems=`mount \
	     | tee -a $seq.full \
	     | sed -e 's/[(,)]//g' \
             | $PCP_AWK_PROG '
$4 == "type" && ($5 == "efs" || $5 == "xfs" || $5 == "ext2" || $5 == "ext3" || $5 == "ext4" || $5 == "btrfs" || $5 == "ffs" || $5 == "vfat") { print $1 }
$4 == "hfs" || $4 == "ufs" { print $1 }'`
echo "filesystems=$filesystems" >>$seq.full

if [ -z "$filesystems" ]
then
    echo "Botch: cannot extract filesystems list from mount(1) output ..."
    mount
    exit
fi

echo "-- df / --" >>$seq.full
rootdev=`df / | tee -a $seq.full | $PCP_AWK_PROG 'NR == 2 { print $1 }'`
echo "rootdev=$rootdev" >>$seq.full
if [ -z "$rootdev" ]
then
    echo "Arrgh, cannot get device for root fs from df(1) ..."
    df /
    exit
fi
if [ -L "$rootdev" ]
then
    if which realpath >/dev/null 2>&1
    then
	symroot=`realpath $rootdev`
    else
	symroot=`ls -l $rootdev | sed -e 's/.*-> //'`
	if [ -z "$symroot" ]
	then
	    echo "Arrgh, cannot get symlink for device for root fs ..."
	    ls -l $rootdev
	    exit
	fi
	case "$symroot"
	in
	    ../*)
		# handle case like ...
		# /dev/disk/by-uuid/467793a8-5191-4940-90fb-38a6f28b035d -> ../../sda1
		dir=`dirname $rootdev`
		while true
		do
		    case "$symroot"
		    in
		    	../*)
			    dir=`echo $dir | sed -e 's@/[^/]*$@@'`
			    symroot=`echo $symroot | sed -e 's@\.\./\([^.]\)@\1@'`
			    ;;
			*)
			    symroot=${dir}/${symroot}
			    break
			    ;;
		    esac
		done
		;;
	esac
    fi
    echo "symroot=$symroot" >>$seq.full
    rootdev=$symroot
fi

# disk name aliases are a real pain ...
#
mount \
| sed \
    -e '/ type proc/d' \
    -e '/ type sysfs/d' \
    -e '/ type devpts/d' \
    -e '/ type devtmpfs/d' \
    -e '/ type tmpfs/d' \
    -e '/ type debugfs/d' \
    -e '/ type autofs/d' \
    -e '/ type securityfs/d' \
    -e '/ type selinuxfs/d' \
    -e '/ type rpc_pipefs/d' \
    -e '/ type fusectl/d' \
    -e '/ type binfmt_misc/d' \
    -e '/ type fuse.gvfs-fuse-daemon/d' \
    -e '/ type cgroup/d' \
    -e '/ type hugetlbfs/d' \
    -e '/ type mqueue/d' \
    -e '/ type configfs/d' \
    -e '/ type tracefs/d' \
    -e '/ type overlay/d' \
    -e '/ type rootfs/d' \
    -e 's/[ 	].*//' \
| while read dev
do
    [ ! -L $dev ] && continue
    [ "$dev" = "/dev/root" ] && continue
    echo "mount device: $dev" >>$seq.full
    if which realpath >/dev/null 2>&1
    then
	sym=`realpath $dev`
    else
	sym=`ls -l $dev | sed -e 's/.*-> //'`
	if [ -z "$sym" ]
	then
	    echo "Arrgh, cannot get symlink for device for fs ..."
	    ls -l $dev
	    exit
	fi
	case "$sym"
	in
	    ../*)
		# handle case like ...
		# /dev/disk/by-uuid/467793a8-5191-4940-90fb-38a6f28b035d -> ../../sda1
		dir=`dirname $dev`
		while true
		do
		    case "$sym"
		    in
		    	../*)
			    dir=`echo $dir | sed -e 's@/[^/]*$@@'`
			    sym=`echo $sym | sed -e 's@\.\./\([^.]\)@\1@'`
			    ;;
			*)
			    sym=${dir}/${sym}
			    break
			    ;;
		    esac
		done
		;;
	esac
    fi
    echo "sym=$sym" >>$seq.full
    link=$sym
    [ "$link" != "$dev" ] && echo "s;\"$dev\";$link;" >>$tmp.map
done

if [ -f $tmp.map ]
then
    # need longest prefix replacements first ... ~ should sort "high"
    #
    sed -e 's/;/~/' $tmp.map \
    | LC_COLLATE=POSIX sort \
    | sed -e 's/~/;/' >$tmp.tmp
    mv $tmp.tmp $tmp.map
else
    echo "s;\"$rootdev\";/dev/root;" >>$tmp.map
fi

if grep "s;$rootdev;" $tmp.map >/dev/null
then
    # already have a mapping for this one
    :
else
    echo "s;\"$rootdev\";/dev/root;" >>$tmp.map
fi

echo "-- map --" >>$seq.full
cat $tmp.map >>$seq.full

numval=`pmprobe -v filesys.avail 2>/dev/null | $PCP_AWK_PROG '{print $2}'`
if [ ! -z "$numval" -a "$numval" -gt 0 ]
then
    # better match for df "free" if available on Linux
    #
    free=filesys.avail
else
    free=filesys.free
fi

echo "-- pcp --" >>$seq.full
pminfo -f filesys >>$seq.full

pminfo -f \
    filesys.capacity \
    filesys.used \
    $free \
    filesys.maxfiles \
    filesys.usedfiles \
    filesys.freefiles \
    filesys.mountdir \
    filesys.full \
| sed \
    -e '/"devfs"/d' \
    -e '/"map -hosts"/d' \
    -e '/"map auto_home"/d' \
    -e "s/$free/filesys.free/" \
    -e 's/\[//g' -e 's/\]//g' \
    -e 's/\"//g' \
| $PCP_AWK_PROG '
/^[ \t]*$/	{next}
/^filesys/	{metric=$1}
$1 == "inst" { printf "%s_%s %s\n", metric, $4, $6 }' \
| LC_COLLATE=POSIX sort > $tmp.1

echo "-- pcp-filtered-1 --" >>$seq.full
cat $tmp.1 >>$seq.full

# map symbolic link names and /dev/root for pcp output
#
sed -f $tmp.map $tmp.1 | LC_COLLATE=POSIX sort >$tmp.tmp
mv $tmp.tmp $tmp.1

echo "-- pcp-filtered-2 --" >>$seq.full
cat $tmp.1 >>$seq.full

(for f in $filesystems
do
    if [ $PCP_PLATFORM = linux -o $PCP_PLATFORM = darwin -o $PCP_PLATFORM = freebsd -o $PCP_PLATFORM = netbsd -o $PCP_PLATFORM = openbsd ]
    then
	$sudo df -lk \
	| grep "^$f " \
	| $PCP_AWK_PROG '
BEGIN		{ dev="'$f'" }
NF == 1		{ next }
NF == 6		{ printf "%s_%s %d\n", "filesys.capacity",  dev, $2
		  printf "%s_%s %d\n", "filesys.used", dev, $3
		  printf "%s_%s %d\n", "filesys.free", dev, $4
		  printf "%s_%s %d\n", "filesys.mountdir", dev, $6
		}
NF == 5		{ printf "%s_%s %d\n", "filesys.capacity",  dev, $1
		  printf "%s_%s %d\n", "filesys.used", dev, $2
		  printf "%s_%s %d\n", "filesys.free", dev, $3
		  printf "%s_%s %d\n", "filesys.mountdir", dev, $5
		}'
	$sudo df -li \
	| grep "^$f " \
	| $PCP_AWK_PROG '
BEGIN		{ dev="'$f'" }
NF == 1		{ next }
NF == 6		{ printf "%s_%s %d\n", "filesys.maxfiles",  dev, $2
		  printf "%s_%s %d\n", "filesys.usedfiles", dev, $3
		  printf "%s_%s %d\n", "filesys.freefiles", dev, $4
		}
NF == 5		{ printf "%s_%s %d\n", "filesys.maxfiles",  dev, $1
		  printf "%s_%s %d\n", "filesys.usedfiles", dev, $2
		  printf "%s_%s %d\n", "filesys.freefiles", dev, $3
		}
NF == 9		{ printf "%s_%s %d\n", "filesys.maxfiles",  dev, $6+$7
		  printf "%s_%s %d\n", "filesys.usedfiles", dev, $6
		  printf "%s_%s %d\n", "filesys.freefiles", dev, $7
		}'
    else
	echo "Blah, what sort df magic is needed for a PCP_PLATFORM $PCP_PLATFORM system?" >&2
	exit
    fi
done) | LC_COLLATE=POSIX sort > $tmp.2

# map symbolic link names and /dev/root for df output
#
sed -f $tmp.map $tmp.2 | LC_COLLATE=POSIX sort >$tmp.tmp
mv $tmp.tmp $tmp.2

echo "-- df -li --" >>$seq.full
$sudo df -li >>$seq.full 2>&1
echo "-- df-filtered --" >>$seq.full
cat $tmp.2 >>$seq.full

LC_COLLATE=POSIX join -a2 -1 1 $tmp.1 $tmp.2 \
| $PCP_AWK_PROG '{
    a=$2; b=$3; d=a-b
    if (d < 0) d = -d
    if (a < 1)
	print $1 " pcp=" a " df=" b " delta=" d  >>"'$seq.full'"
    else
	printf "%s pcp=%d df=%d delta=%d (%.2f%%)\n",$1,a,b,d,d*100/a >>"'$seq.full'"
    # skip small values ... the 1% threshold is meaningless ...
    if (a < 100 || b < 100) next
    if (d > 5 && d > a/100) {
	print "'$0' : Diff of more than 5 in total and 1 percent: ", $0
	err++
    }
}
END {
    exit err
}'

if [ $? != 0 ]
then
    echo $0 =========== pcp output ==============
    cat $tmp.1
    echo $0 =========== df output ==============
    cat $tmp.2
fi

exit
